-
Notifications
You must be signed in to change notification settings - Fork 214
mcp/tool: Using Reflection For ApplySchema #510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mcp/tool: Using Reflection For ApplySchema #510
Conversation
Thanks for this big and impressive effort! You should always file an issue before attempting this scale of work, even if there is a TODO in the code. In this case, I'm not really sure what problem this is solving. Perhaps @findleyr has a better sense. |
ups sorry @jba @findleyr, this is my first time i contribute so i miss this |
Let's start with my comment in mcp/reflection_validator.go. I don't understand what this PR provides. |
the TODO "use reflection to create the struct type to unmarshal into"
So I create ReflectionValidator as the base/main wrapper for schema validation.
For SchemaTypeBuilder:
here's the idea of the PR, let me know if need to change something or remove the cache key implementation @jba . |
It seems like you just summarized what this PR does without answering my question. The question is: Wouldn't resolved.Validate(mapData) catch all the errors that this would catch? (see my comment on reflection_validator.go) |
Ups Sorry, I couldn’t find your comment—maybe it wasn’t submitted yet? @jba |
structValue := reflect.New(structType) | ||
structPtr := structValue.Interface() | ||
|
||
// Unmarshal directly into the typed struct for reflection-based type validation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the part I don't understand. What is the value of the struct? Wouldn't resolved.Validate(mapData) catch all the errors that this would catch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What i'm understand is current implementation is apply to a generic map and re-marshalled
meaning that:
it not considering Go struct field tags such as ",string". As a result, a numeric default remains a JSON number, which cannot be unmarshalled into a field expecting a string-encoded number.
Can Try Below Unit Test Or Refer To Image
func TestApplySchema_DefaultTypeMismatchWithStringTag(t *testing.T) {
schema := &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
// Note: default is a JSON number (3), not a quoted string.
"n": {Type: "integer", Default: json.RawMessage("3")},
},
}
resolved, err := schema.Resolve(&jsonschema.ResolveOptions{ValidateDefaults: true})
if err != nil {
t.Fatalf("resolve schema: %v", err)
}
// No arguments provided; expect defaults to be applied.
var input json.RawMessage = json.RawMessage(`{}`)
input, err = applySchema(input, resolved)
if err != nil {
t.Fatalf("applySchema: %v", err)
}
// Struct expects a string-encoded number because of ",string" tag.
type In struct {
N int `json:"n,string"`
}
var got In
// Current behavior: json.Unmarshal will fail with:
// cannot unmarshal number into Go struct field In.n of type string
// because the JSON contains {"n":3} after defaults, but the field expects a string.
if err := json.Unmarshal(input, &got); err == nil {
t.Fatalf("expected unmarshal to fail due to ,string tag with numeric default; got success with value: %+v", got)
} else {
// Tighten expectation to document the exact failure mode.
if !strings.Contains(err.Error(), "cannot unmarshal number into Go struct field") {
t.Fatalf("unexpected unmarshal error: %v", err)
}
}
}

That's why we need reflection
or can we close this PR first? let me clean up all this first into a proposal and raise an issue for this @jba
will raise a issue first before we proceed into PR later on |
Requirement
// TODO: use reflection to create the struct type to unmarshal into.
// Separate validation from assignment.
// Use default JSON marshalling for validation.
//
// This avoids inconsistent representation due to custom marshallers, such as
// time.Time (issue #449).
//
// Additionally, unmarshalling into a map ensures that the resulting JSON is
// at least {}, even if data is empty. For example, arguments is technically
// an optional property of callToolParams, and we still want to apply the
// defaults in this case.
//
// TODO(rfindley): in which cases can resolved be nil?
New Files:
Modified: